// 编程实现采集传感器数据和控制硬件设备（传感器和硬件通过slave模拟）
// 	传感器：2个，光线传感器、加速度传感器（x\y\z）
// 	硬件设备：2个，led灯、蜂鸣器
// 要求：
// 1.多任务编程：多线程
// 2.循环1s采集一次数据，并将数据打印至终端
// 3.同时从终端输入指令控制硬件设备
// 0  1: led灯打开
// 0  0: led灯关闭
// 1  1: 蜂鸣器开
// 1  0: 蜂鸣器关

#include <stdio.h>
#include <modbus.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <stdint.h>
#include <sys/msg.h>

typedef struct
{
    long type;
    char addr;
    char comd;
} msg_t;

void *handler(void *arg)
{
    modbus_t *ctx = (modbus_t *)arg;
    uint16_t data[4] = {0};
    key_t key;
    while ((key = ftok("./key", 'g')) == -1)
        ;

    // 创建或打开共享内存
    int shmid;
    // shmid = shmget(key, 256, IPC_CREAT | IPC_EXCL | 0666);
    while ((shmid = shmget(key, 256, IPC_CREAT | IPC_EXCL | 0666)) <= 0)
    {
        if (errno == EEXIST) //如果已存在则直接打开共享内存
        {
            shmid = shmget(key, 128, 0666); //直接打开共享内存
            break;
        }
    }

    // 映射共享内存
    uint16_t *p = NULL;
    while ((p = (uint16_t *)shmat(shmid, NULL, 0)) == (uint16_t *)-1)
        ;
    // 每隔1s采集一次传感器数据，并打印
    p[4] = 0;
    while (1)
    {
        if (p[4] == 0)
        {
            modbus_read_registers(ctx, 0, 4, data);
            p[0] = data[0];
            p[1] = data[1];
            p[2] = data[2];
            p[3] = data[3];
            p[4] = 1;
            printf("灯 光 传 感 器 ：%d\n", data[0]);
            printf("x轴加速度传感器：%d\n", data[1]);
            printf("y轴加速度传感器：%d\n", data[2]);
            printf("z轴加速度传感器：%d\n", data[3]);
        }
    }

    //取消映射
    shmdt(p);

    //删除共享内存
    // shmctl(shmid, IPC_RMID, NULL);
    return NULL;
}

int main(int argc, char const *argv[])
{
    // 1.创建实例   modbus_new_tcp
    modbus_t *ctx = modbus_new_tcp("192.168.0.114", 502);
    if (ctx == NULL)
    {
        perror("modbus_new_tcp err");
        return -1;
    }
    // 2.设置从机ID  modbus_set_slave
    modbus_set_slave(ctx, 1);
    // 3.建立连接 modbus_connect
    if (modbus_connect(ctx) == -1)
    {
        perror("modbus_connect err");
        return -1;
    }

    // 4.寄存器操作 根据不同的功能选对应函数
    // 从线程 -------- 采集数据并打印
    pthread_t tid;
    pthread_create(&tid, NULL, handler, ctx);
    pthread_detach(tid);

    // 主线程 -------- 输入指令
    // 0  1: led灯打开
    // 0  0: led灯关闭
    // 1  1: 蜂鸣器开
    // 1  0: 蜂鸣器关

    // 消息队列
    key_t key;
    while ((key = ftok("./key", 'g')) == -1)
        ;

    int msgid;
    while ((msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0666)) <= 0)
    {
        if (errno == EEXIST)
        {
            msgid = msgget(key, 0666);
            break;
        }
    }

    msg_t msg;
    while (1)
    {
        msgrcv(msgid, &msg, sizeof(msg) - sizeof(long), 0, 0);
        modbus_write_bit(ctx, (int)msg.addr - 48, (int)msg.comd - 48);
    }

    // // 5.关闭套接字 modbus_close
    // modbus_close(ctx);
    // // 6.释放实例 modbus_free
    // modbus_free(ctx);

    return 0;
}
